﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;

namespace ServiceOperater.Helpers
{
    // http://www.sharejs.com/codes/csharp/7349
    class RxGrep
    {
        public static int skipmatches = 0;
        public static int takematches = 0;

        static int Main1(string[] args)
        {
            try
            {
                #region Command Line Parsing

                // Check for /? on the command line
                foreach (string arg in args)
                {
                    if (arg == "/?")
                    {
                        return WriteError();
                    }
                }

                string filename = string.Empty;
                string pattern = string.Empty;
                string[] options = null;
                RegexOptions regexoptions = RegexOptions.None;
                bool isredirected = ConsoleEx.InputRedirected;
                int redirectnum = (isredirected ? 1 : 0);
                string input = string.Empty;

                // Check and interpret command line arguments
                switch (args.Length + redirectnum)
                {
                    case 2:
                        if (isredirected)
                        {
                            pattern = args[0];
                        }
                        else
                        {
                            filename = args[0];
                            pattern = args[1];
                        }
                        break;
                    case 3:
                    case 4:
                    case 5:
                        if (isredirected)
                        {
                            pattern = args[0];
                            options = args.Slice(1, args.Length);
                        }
                        else
                        {
                            filename = args[0];
                            pattern = args[1];
                            options = args.Slice(2, args.Length);
                        }
                        break;
                    default:
                        return WriteError();
                }

                if (options != null)
                {
                    foreach (string option in options)
                    {
                        // Right now, /I is the only valid command line switch
                        switch (option.ToUpper().Substring(0, 2))
                        {
                            case "/I":
                                regexoptions |= RegexOptions.IgnoreCase;
                                break;
                            case "/S":
                                try
                                {
                                    skipmatches = Convert.ToInt32(option.Substring(3));
                                }
                                catch (Exception e)
                                {
                                    Console.Error.WriteLine("Error: {0}", e.Message);
                                    return WriteError("Invalid command line switch: " + option);
                                }
                                break;
                            case "/T":
                                try
                                {
                                    takematches = Convert.ToInt32(option.Substring(3));
                                }
                                catch (Exception e)
                                {
                                    Console.Error.WriteLine("Error: {0}", e.Message);
                                    return WriteError("Invalid command line switch: " + option);
                                }
                                break;
                            default:
                                return WriteError("Invalid command line " + (option.Substring(0, 1) == "/" ? "switch" : "argument") + ": " + option);
                        }
                    }
                }

                if (isredirected)
                {
                    // Read the redirected Standard Input
                    input = Console.In.ReadToEnd();
                }
                else
                {
                    // Check if the file name is valid
                    if (filename.IndexOf("/") > -1)
                    {
                        return WriteError();
                    }
                    if (filename.IndexOfAny("?*".ToCharArray()) > -1)
                    {
                        return WriteError("Wildcards not allowed");
                    }
                    // Check if the file exists
                    if (File.Exists(filename))
                    {
                        // Read the file content
                        using (StreamReader file = new StreamReader(filename))
                        {
                            input = file.ReadToEnd();
                        }
                    }
                    else
                    {
                        return WriteError("File not found: \"" + filename + "\"");
                    }
                }

                #endregion Command Line Parsing


                // Now that the command line parsing is done, let's get some action
                if (DisplayMatches(input, pattern, regexoptions) == 0)
                {
                    return WriteError("No match found");
                }
                else
                {
                    return 0;
                }
            }
            catch (Exception e)
            {
                return WriteError(e.Message);
            }
        }


        // The main functionality: display all matching substrings
        public static int DisplayMatches(string haystack, string needle, RegexOptions options)
        {
            int counter = 0;
            int displayed = 0;
            // Get all matches
            MatchCollection matches = Regex.Matches(haystack, needle, options);
            if (matches.Count > skipmatches)
            {
                foreach (Match match in matches)
                {
                    if (counter >= skipmatches && (displayed < takematches || takematches == 0))
                    {
                        Console.WriteLine(match.Value);
                        displayed += 1;
                    }
                    counter += 1;
                }
            }
            return displayed;
        }


        #region Redirection Detection

        // Code to detect redirection by Hans Passant on StackOverflow.com
        // http://stackoverflow.com/questions/3453220/how-to-detect-if-console-in-stdin-has-been-redirected
        public static class ConsoleEx
        {
            public static bool OutputRedirected
            {
                get
                {
                    return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stdout));
                }
            }

            public static bool InputRedirected
            {
                get
                {
                    return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stdin));
                }
            }

            public static bool ErrorRedirected
            {
                get
                {
                    return FileType.Char != GetFileType(GetStdHandle(StdHandle.Stderr));
                }
            }

            // P/Invoke:
            private enum FileType { Unknown, Disk, Char, Pipe };
            private enum StdHandle { Stdin = -10, Stdout = -11, Stderr = -12 };

            [DllImport("kernel32.dll")]
            private static extern FileType GetFileType(IntPtr hdl);

            [DllImport("kernel32.dll")]
            private static extern IntPtr GetStdHandle(StdHandle std);
        }

        #endregion Redirection Detection

        #region Error Handling

        public static int WriteError(Exception e = null)
        {
            return WriteError(e == null ? null : e.Message);
        }

        public static int WriteError(string errorMessage)
        {
            Console.OpenStandardError();
            if (string.IsNullOrEmpty(errorMessage) == false)
            {
                Console.Error.WriteLine();
                Console.ForegroundColor = ConsoleColor.Red;
                Console.Error.Write("ERROR: ");
                Console.ForegroundColor = ConsoleColor.White;
                Console.Error.WriteLine(errorMessage);
                Console.ResetColor();
            }

            /*
            RxGrep,  Version 2.00
            Multi-line FindStr/Grep like tool

            Usage:   RXGREP  filename  pattern  [ /I ]  [ /S:nn ]  [ /T:nn ]
            or:      some_command  |  RXGREP  pattern  [ /I ]  [ /S:nn ]  [ /T:nn ]

            Where:   filename       is the file to be filtered
                     some_command   is the command whose standard output is to be filtered
                     pattern        is the search pattern (regular expression)
                     /I             makes the search case insensitive
                     /S:nn          Skip the first nn matches
                     /T:nn          Take only nn matches

            Example: ROBOCOPY D:\sourcedir E:\targetdir /NP /MIR |
                     RXGREP "\s+\d+\s+D:\\sourcedir\\[^\n\r]*\r\n([^\n\r\\]+\r\n)+"
                     (to be read as a single command line) will return something like:
                                     125    D:\sourcedir\subdir\
                        New File                 342        brandnewfile.ext
                        Newer                  4.06m        updatedfile.ext
                      *EXTRA File              2.40m        deletedfile.ext

            Check for redirection by Hans Passant on StackOverflow.com
            /questions/3453220/how-to-detect-if-console-in-stdin-has-been-redirected
            Array Slice extension by Sam Allen http://www.dotnetperls.com/array-slice

            Written by Rob van der Woude
            http://www.robvanderwoude.com
             */

            Console.Error.WriteLine();
            Console.Error.WriteLine("RxGrep,  Version 2.00");
            Console.Error.WriteLine("Multi-line FindStr/Grep like tool");
            Console.Error.WriteLine();
            Console.Error.Write("Usage:   ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.WriteLine("RXGREP  filename  pattern  [ /I ]  [ /S:nn ]  [ /T:nn ]");
            Console.ResetColor();
            Console.Error.Write("or:      ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.WriteLine("some_command  |  RXGREP  pattern  [ /I ]  [ /S:nn ]  [ /T:nn ]");
            Console.ResetColor();
            Console.Error.WriteLine();
            Console.Error.Write("Where:   ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.Write("filename");
            Console.ResetColor();
            Console.Error.WriteLine("       is the file to be filtered");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.Write("         some_command");
            Console.ResetColor();
            Console.Error.WriteLine("   is the command whose standard output is to be filtered");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.Write("         pattern");
            Console.ResetColor();
            Console.Error.WriteLine("        is the search pattern (regular expression)");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.Write("         /I");
            Console.ResetColor();
            Console.Error.Write("             makes the search case ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.Write("I");
            Console.ResetColor();
            Console.Error.WriteLine("nsensitive");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.Write("         /S:nn          S");
            Console.ResetColor();
            Console.Error.Write("kip the first ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.Write("nn");
            Console.ResetColor();
            Console.Error.WriteLine(" matches");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.Write("         /T:nn          T");
            Console.ResetColor();
            Console.Error.Write("ake only ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.Write("nn");
            Console.ResetColor();
            Console.Error.WriteLine(" matches");
            Console.Error.WriteLine();
            Console.Error.Write("Example: ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.WriteLine(@"ROBOCOPY D:\sourcedir E:\targetdir /NP /MIR |");
            Console.Error.WriteLine(@"         RXGREP ""\s+\d+\s+D:\\sourcedir\\[^\n\r]*\r\n([^\n\r\\]+\r\n)+""");
            Console.ResetColor();
            Console.Error.WriteLine("         (to be read as a single command line) will return something like:");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Error.WriteLine(@"                         125    D:\sourcedir\subdir\");
            Console.Error.WriteLine("            New File                 342        brandnewfile.ext");
            Console.Error.WriteLine("            Newer                  4.06m        updatedfile.ext");
            Console.Error.WriteLine("          *EXTRA File              2.40m        deletedfile.ext");
            Console.ResetColor();
            Console.Error.WriteLine();
            Console.Error.Write("Check for redirection by Hans Passant on ");
            Console.ForegroundColor = ConsoleColor.DarkGray;
            Console.Error.WriteLine("StackOverflow.com");
            Console.Error.WriteLine("/questions/3453220/how-to-detect-if-console-in-stdin-has-been-redirected");
            Console.ResetColor();
            Console.Error.Write("Array Slice extension by Sam Allen ");
            Console.ForegroundColor = ConsoleColor.DarkGray;
            Console.Error.WriteLine("http://www.dotnetperls.com/array-slice");
            Console.ResetColor();
            Console.Error.WriteLine();
            Console.Error.WriteLine("Written by Rob van der Woude");
            Console.Error.Write("http://www.robvanderwoude.com");
            Console.OpenStandardOutput();
            return 1;
        }

        #endregion Error Handling
    }

    #region Extensions

    // Array Slice
    // http://www.dotnetperls.com/array-slice
    public static class Extensions
    {
        /// <summary>
        /// Get the array slice between the two indexes.
        /// ... Inclusive for start index, exclusive for end index.
        /// </summary>
        public static T[] Slice<T>(this T[] source, int start, int end)
        {
            // Handles negative ends.
            if (end < 0)
            {
                end = source.Length + end;
            }
            int len = end - start;

            // Return new array.
            T[] res = new T[len];
            for (int i = 0; i < len; i++)
            {
                res[i] = source[i + start];
            }
            return res;
        }
    }

    #endregion Extensions
}


